package com.example.hello.recommender;

import com.example.hello.pojo.Interaction;

import java.util.*;
import java.util.stream.Collectors;
import static java.lang.Math.sqrt;

public class CFRecommender {

    private static final double FAVORITE_WEIGHT = 2.0;
    private static final double BORROW_WEIGHT = 0.5;
    private static final double SIMILARITY_THRESHOLD = 0.2; // 新增相似度阈值

    private Map<Integer, Map<Integer, Double>> userBookMatrix;
    private Map<Integer, Double> userAverageRating;  // 新增用户平均评分缓存
    private Map<Integer, Integer> bookPopularity;    // 新增书籍热门度缓存

    public CFRecommender(List<Interaction> interactions) {
        userBookMatrix = new HashMap<>();
        userAverageRating = new HashMap<>();
        bookPopularity = new HashMap<>();

        Map<Integer, Integer> userRatingCounts = new HashMap<>();

        for (Interaction inter : interactions) {
            double score = computeScore(inter);
            userBookMatrix.computeIfAbsent(inter.getUserId(), k -> new HashMap<>())
                    .put(inter.getBookId(), score);

            // 计算用户评分和，用于计算用户评分均值
            userAverageRating.put(inter.getUserId(), userAverageRating.getOrDefault(inter.getUserId(), 0.0) + score);
            userRatingCounts.put(inter.getUserId(), userRatingCounts.getOrDefault(inter.getUserId(), 0) + 1);

            // 计算书籍热门度
            bookPopularity.put(inter.getBookId(), bookPopularity.getOrDefault(inter.getBookId(), 0) + 1);
        }

        // 计算用户平均评分
        for (Map.Entry<Integer, Double> entry : userAverageRating.entrySet()) {
            Integer userId = entry.getKey();
            userAverageRating.put(userId, entry.getValue() / userRatingCounts.get(userId));
        }
    }

    private double computeScore(Interaction interaction) {
        double favoriteScore = interaction.isFavorite() ? FAVORITE_WEIGHT : 0.0;
        double borrowScore = interaction.getBorrowCount() * BORROW_WEIGHT;
        return interaction.getRating() + favoriteScore + borrowScore;
    }

    private double cosineSimilarity(Map<Integer, Double> map1, Map<Integer, Double> map2) {
        Set<Integer> commonBooks = new HashSet<>(map1.keySet());
        commonBooks.retainAll(map2.keySet());
        if (commonBooks.isEmpty()) return 0.0;
        double dotProduct = 0.0;
        for (Integer bookId : commonBooks) {
            dotProduct += map1.get(bookId) * map2.get(bookId);
        }
        double norm1 = sqrt(map1.values().stream().mapToDouble(x -> x * x).sum());
        double norm2 = sqrt(map2.values().stream().mapToDouble(x -> x * x).sum());
        return (norm1 * norm2 != 0) ? dotProduct / (norm1 * norm2) : 0.0;
    }

    // 增加相似度阈值过滤
    public Map<Integer, Double> getUserSimilarities(Integer targetUserId) {
        Map<Integer, Double> similarities = new HashMap<>();
        Map<Integer, Double> targetRatings = userBookMatrix.get(targetUserId);
        if (targetRatings == null) return similarities;

        for (Map.Entry<Integer, Map<Integer, Double>> entry : userBookMatrix.entrySet()) {
            Integer otherUserId = entry.getKey();
            if (otherUserId.equals(targetUserId)) continue;

            double sim = cosineSimilarity(targetRatings, entry.getValue());
            if (sim > SIMILARITY_THRESHOLD) {  // 应用阈值
                similarities.put(otherUserId, sim);
            }
        }
        return similarities;
    }

    // 优化预测评分方法，考虑评分偏置
    public Double predictRating(Integer targetUserId, Integer bookId) {
        Map<Integer, Double> similarities = getUserSimilarities(targetUserId);
        double numerator = 0.0;
        double denominator = 0.0;

        for (Map.Entry<Integer, Double> entry : similarities.entrySet()) {
            Integer otherUserId = entry.getKey();
            double sim = entry.getValue();
            Map<Integer, Double> otherRatings = userBookMatrix.get(otherUserId);
            if (otherRatings != null && otherRatings.containsKey(bookId)) {
                double ratingDiff = otherRatings.get(bookId) - userAverageRating.get(otherUserId);
                numerator += sim * ratingDiff;
                denominator += Math.abs(sim);
            }
        }
        return (denominator != 0) ? userAverageRating.get(targetUserId) + numerator / denominator : null;
    }

    // 冷启动问题处理：如果无法预测，推荐最热门书籍
    public List<Map.Entry<Integer, Double>> recommendBooks(Integer targetUserId, List<Integer> allBookIds, int topN) {
        Map<Integer, Double> targetRatings = userBookMatrix.getOrDefault(targetUserId, Collections.emptyMap());
        List<Integer> candidateBooks = allBookIds.stream()
                .filter(bookId -> !targetRatings.containsKey(bookId))
                .collect(Collectors.toList());

        Map<Integer, Double> predictions = new HashMap<>();
        for (Integer bookId : candidateBooks) {
            Double predictedRating = predictRating(targetUserId, bookId);
            if (predictedRating != null) {
                predictions.put(bookId, predictedRating);
            }
        }

        // 冷启动处理：如果预测结果为空，推荐热门书籍
        if (predictions.isEmpty()) {
            return candidateBooks.stream()
                    .sorted((b1, b2) -> Integer.compare(bookPopularity.getOrDefault(b2, 0),
                            bookPopularity.getOrDefault(b1, 0)))
                    .limit(topN)
                    .map(bookId -> new AbstractMap.SimpleEntry<>(bookId, 0.0))  // 0.0表示未知预测评分
                    .collect(Collectors.toList());
        }

        return predictions.entrySet().stream()
                .sorted((e1, e2) -> Double.compare(e2.getValue(), e1.getValue()))
                .limit(topN)
                .collect(Collectors.toList());
    }
}